<?php
namespace Core;

final class HHO {
	/**
	 * HHO和应用程序的配置信息
	 * @var array
	 * @access public
	 */
	public static $config = array();
	/**
	 * 最近一个请求的控制器名称，参考 HHO::$thread 属性
	 * @var string
	 * @access public
	 */
	public static $controller = '';
	/**
	 * 最近一个请求的操作名称，参考 HHO::$thread 属性
	 * @var string
	 * @access public
	 */
	public static $action = '';
	
	/**
	 * 当前客户端请求中所有正在执行的请求的列表，其中包括内部请求，而一个请求执行完成后将立即从该列表中注销
	 * @var array
	 * @access private
	 */
	private static $thread = array();

	/**
	 * 禁止实例化 Core\HHO 对象
	 *
	 */
	private function __construct() {}

	/**
	 * 请求初始化，其中包括以下几个动作：
	 * - 加载HHO配置
	 * - 对当前PHP环境进行相关设置
	 * - 部分HHO系统常量的定义
	 *
	 * @return void
	 */
	static public function init() {
		self::config(HHO_CONFIG());
		self::config(HHO_APP_CONFIG());
		
		set_error_handler(__NAMESPACE__ . '\Error::handle');
		
		spl_autoload_register('self::import');
		
		defined('HHO_APP_NAME') && HHO_APP_NAME || trigger_error('Unknown application name', E_USER_ERROR);
		
		date_default_timezone_set(self::$config['timezone']);
		
		self::$config['session'] && session_start();
		
		self::dispatch();
		
		self::run(HHO_CONTROLLER, HHO_ACTION);
	}

	/**
	 * 请求调度器，包括以下动作：
	 * - 若开启了 PATHINFO 重写功能，则获得重写后的 PATHINFO
	 * - 尝试通过 $_REQUEST 获得请求的控制器和操作
	 * - 尝试通过 PATHINFO 获得请求的控制器和操作
	 * - 注册 PATHINFO 中的变量至 $_GET 和 $_REQUEST
	 * - 若无法获得请求的控制器和操作，则请求默认控制器和操作
	 *
	 * @return void
	 */
	static private function dispatch() {
		$info = isset($_SERVER['PATH_INFO']) ? $_SERVER['PATH_INFO'] : '';
		
		self::$config['rewrite'] && include HHO_APP_PATH . '/Config/Rewrite.php';
		
		$info = ltrim(preg_replace('#^(.*)\.html$#i', '$1', $info), '/');
		
		$slice = $info ? preg_split('/' . '\\' . wordwrap(self::$config['tick'], 1, '|\\', true) . '/', $info) : array();
		
		$controller = isset($_REQUEST['c']) ? $_REQUEST['c'] : (isset($slice[0]) ? array_shift($slice) : '');
		$action = isset($_REQUEST['a']) ? $_REQUEST['a'] : (isset($slice[0]) ? array_shift($slice) : '');
		
		// 路由器中所设置的变量或 PATHINFO 中所传递的变量
		$variable = array();
		
		if (isset($slice[0])) {
			for ($i = 0, $j = count($slice); $i < $j;) {
				$variable[$slice[$i++]] = isset($slice[$i]) ? $slice[$i++] : '';
			}
		}
		
		// 按照 PHP 的 request_order 配置参数将变量添加至 $_REQUEST 超全局变量中
		$varorder = strtoupper(ini_get('request_order'));
		
		foreach ($variable as $name => $value) {
			if (!isset($_GET[$name])) { // 优先URL查询串中出现的同名变量
				$_GET[$name] = get_magic_quotes_gpc() ? addslashes($value) : $value;
				
				for ($i = 0; $i < strlen($varorder); $i++) {
					switch ($varorder[$i]) {
						case 'G' :
							$_REQUEST[$name] = $_GET[$name];
							break;
						case 'P' :
							isset($_POST[$name]) && $_REQUEST[$name] = $_POST[$name];
							break;
						case 'C' :
							isset($_COOKIE[$name]) && $_REQUEST[$name] = $_COOKIE[$name];
							break;
					}
				}
			}
		}
		
		self::pascal($controller, $action);
		
		define('HHO_CONTROLLER', $controller ?  : self::$config['controller.default']);
		define('HHO_ACTION', $action ?  : self::$config['action.default']);
	}

	/**
	 * 将下划线命名法形式的控制器名和操作名转为帕斯卡（Pascal）命名法形式
	 *
	 * @param string &$controller
	 * @param string &$action
	 * @return void
	 */
	static private function pascal(&$controller, &$action) {
		if ($controller) {
			$controller = str_replace(str_split(self::$config['tick']), '~', $controller);
			$controller = str_replace(array(' ', "\t"), array('\\', ''), ucwords(str_replace(array('~', '_'), array(' ', "\t"), $controller)));
			
			substr($controller, -1) == '\\' && $controller .= self::$config['controller.default'];
		}
		
		$action && $action = str_replace(' ', '', ucwords(str_replace('_', ' ', $action)));
	}

	/**
	 * 执行一个控制器的一个操作
	 *
	 * @param string $controller
	 * @param string $action
	 * @return boolean
	 */
	static public function run($controller, $action) {
		self::pascal($controller, $action);
		
		// 必须在控制器类被实例化之前明确 self::$controller 和 self::$action 的值，因为在控制器类的构造方法中可能会使用到
		self::$controller = $controller;
		self::$action = $action;
		
		$class = 'Controller\\' . $controller;
		
		if (!self::import($class)) {
			$class = 'Controller\\' . self::$config['controller.void'];
			
			if (!self::import($class)) {
				Error::h404("Controller '{$controller}' not found");
			}
		}
		
		HHO::$thread[] = array($controller, $action);
		
		// 控制器默认使用单实例模式，多实例模式的实现见 Core\Controller::getInstance() 方法的注释内容
		$class::getInstance()->$action();
		
		array_pop(HHO::$thread);
		
		list(self::$controller, self::$action) = end(HHO::$thread);
	}

	/**
	 * 导入一个类，类的命名空间描述将被解析为类文件的路径
	 *
	 * @param string $class
	 * @return boolean
	 */
	static private function import($class) {
		static $import = array();
		
		preg_match('/^(\w+)\\\\(.+)$/i', $class, $info);
		
		switch ($info[1]) {
			case 'Model' :
				$dir = &self::$config['model.root'];
				break;
			case 'Dao' :
				$dir = &self::$config['dao.root'];
				break;
			case 'Controller' :
				$dir = &self::$config['controller.root'];
				break;
			case 'Core' :
				$dir = HHO_PATH . '/Core';
				break;
			case 'Lib' :
				$dir = HHO_PATH . '/Lib';
				break;
			default :
				$dir = HHO_APP_PATH . '/' . dirname(str_replace('\\', '/', $class));
		}
		
		if (!isset($import[$class]) || !$import[$class]) {
			$files = $dir . '/' . str_replace('\\', '/', $info[2]) . '.php';
			
			is_file($files) && include $files;
			
			$import[$class] = class_exists($class, FALSE);
			
			$import[$class] || trigger_error("HHO::import($class) failed", E_USER_WARNING);
		}
		
		return isset($import[$class]) ? $import[$class] : FALSE;
	}

	/**
	 * 添加，修改或获取 HHO 的配置项
	 *
	 * @param string|array
	 * @param mixed $value
	 * @return mixed
	 */
	static public function config($name, $value = null) {
		if (is_array($name)) { // 若 $name 为数组，则调用 self::config() 方法的动作一定是添加或修改配置
			self::$config = $name + self::$config;
			
			return true;
		}
		
		if (is_null($value)) {
			return self::$config[$name];
		}
		
		self::$config[$name] = $value;
		
		return true;
	}
}